#include "utils.h"
#include "Image.h"

/* cr�ation d'un image de widthxheight pixels avec nChannels cannaux et profondeur depth*/
sImage* CreatesImage(uint width, uint height, uint nChannels, uint depth)
{
	sImage* im=(sImage*) MyAlloc(sizeof(sImage));
	im->width=width;
	im->height=height;
	im->nChannels=nChannels;
	/*im->depth=depth;*/
	/* on impose depth=1 -> 1 octet par cannal*/
	im->depth=1;
	im->ImageData=(uchar*)MyAlloc(depth*width*height*nChannels);
	return im;
}

/* lib�ration de l'espace m�moire*/
void ReleasesImage(sImage* im)
{
	if (im)
	{
		free(im->ImageData);
		free(im);
	}
	else
	{
		Error("Freeing an empty space");
	}
}

/* lecture d'un image pnm*/
sImage* LoadImageFromPnm(const char* filename)
{
	uint type;
	char c;
	uint width;
	uint height;
	uint nChannels;
	uint depth=1;
	uint val_max=1;
	uchar* ptr;
	int i=0, j=0, k=0;
	sImage* im=NULL;
	
	FILE* fd = MyFopen(filename, "r");
	
	/* on lit le nombre magique*/
	
	c=MyFgetc(fd); /* lecture du P*/
	if (c!='P')
		Error("The file is not PNM compliant");
	c=MyFgetc(fd); /* lecture du type*/
	type=c-'0';
	if (type <1|| type >6)
		Error("The file is not PNM compliant");
	/* on lit ensuite la r�solution*/
	width=(uint)MyFgeti(fd);
	height=(uint)MyFgeti(fd);
	/* nombre de cannaux: si P1 P2 P4 P5 ->1 cannal, sinon avec P3 et P6 -> 3 cannaux*/
	
	if (type==PPM_BINARY || type==PPM_ASCII)
	{
		nChannels=3;
	}
	else
	{
		nChannels=1;
	}
	
	/* on lit la quantification pour tout type d'image sauf PBM*/
	
	if (type!=PBM_BINARY && type!=PBM_ASCII)
	{
		if (MyFgeti(fd) >255)
		{
			Error("Depth > 8 bits is not supported yet");
		}
		else
		{
			depth=1;
			val_max=255;
		}
	}
	/* on alloue l'image*/
	im=CreatesImage(width,height,nChannels,depth);
	/* maintenant on r�cup�re les donn�es de densit�*/
	
	/* Version PBM_ASCII*/
	int tmp;
	if (type == PBM_ASCII ||  type==PGM_ASCII || type==PPM_ASCII)
	{
		for (i=0; i<height; ++i)
		{
			for (j=0; j<width; ++j)
			{
				ptr=GetPix(im,j,i);
				for (k=0; k<nChannels; ++k)
				{
					tmp=MyFgeti(fd);
					if (tmp > val_max)
					{
						Error("Corrupted file!");
					}
					ptr[k]=(uchar)tmp;
				}
			}
		}
	}
	else
	/* Version PBM_BINARY*/
	if (type == PBM_BINARY)
	{
		/* Cas particulier : */
		/* un octet lu contient les densit�s de 8 pixels*/
		uchar* ptr=im->ImageData;
		uchar c;
		uchar one=1;
		
		while (ptr <= im->ImageData+im->width*im->height)
		{
			/* on lit un octet*/
			fread (&c,1,1,fd);
			for (i=7; i >=0;i--)
			{
				/* ptr[0] = 1 si le i�me bit de c est � 1*/
				ptr[0]=((c & (one<<i))>0);
				
				if (ptr > im->ImageData+im->width*im->height)
					break;
				
				ptr++;
			}
		}
	}
	else
	{
		fread (im->ImageData,im->depth,height*width*nChannels,fd);
	}
	fclose(fd);
	return im;
}

/* sauvegarde de l'image au format pnm*/
void  SaveImageToPnm(sImage* im,const char* filename, uint type)
{
	int i,j,k;
	uchar* ptr;
	if (im == NULL)
	{
		Error("Cannot save a non initialized image");
	}
	/* on v�rifie que le format donn� est compatible avec l'image :*/
	if (im->nChannels==3)
	{
		if (type != PPM_ASCII && type != PPM_BINARY)
			Error("Internal image format is not compatible with the requested output format");
	}
	else
	{
		if (type == PPM_ASCII || type == PPM_BINARY)
			Error("Internal image format is not compatible with the requested output format");
	}
	
	FILE* fd = MyFopen(filename, "w");
	fprintf(fd,"P%u\n",type);
	fprintf(fd,"#Created by XY et SG\n");
	fprintf(fd,"%u %u\n",im->width, im->height);
	if (type != PBM_ASCII &&  type!=PBM_BINARY)
	{
		fprintf(fd,"255\n");
	}
	/* maintenant on sauvegarde les donn�es*/
	
	/************************************************************************************************/
	
	/*Cette partie est � completer*/
	
	/* Version PBM_ASCII*/
	
	if (type == PBM_ASCII ||  type==PGM_ASCII || type==PPM_ASCII)
	{
		for (i=0; i<im->height; ++i)
		{
			for (j=0; j<im->width; ++j)
			{
				ptr=GetPix(im,j,i);
				for (k=0; k<im->nChannels; ++k)
				{
					fprintf(fd,"%i ",ptr[k]);
				}
				
			}
			fprintf(fd,"\n");
		}
	}
	else
	/* Version PBM_BINARY*/
	if (type == PBM_BINARY)
	{
		/* Cas particulier : */
		/* un octet lu contient les densit�s de 8 pixels*/
		uchar* ptr=im->ImageData;
		uchar c;
		uchar one=1;
		
		while (ptr <= im->ImageData+im->width*im->height)
		{
			c=0;
			/* on lit un octet*/
			for (i=7; i >=0;i--)
			{
				/* ptr[0] = 1 si le i�me bit de c est � 1*/
				if (ptr[0])
				{
					c+=(one<<i);
				}
				ptr++;
			}
			fwrite (&c,1,1,fd);
		}
	}
	else
	{
		fwrite (im->ImageData,im->depth,im->height*im->width*im->nChannels,fd);
	}
	fclose(fd);
}

/* retourne un pointeur sur le pixel plac� en x et y*/
/* attention cette fonction n'est pas vraie dans l'absolu !!! Vrai uniquement si depth = 8 !!! */
uchar* GetPix(sImage* im, uint x, uint y)
{
	if (x<im->width && y<im->height)
	{
		return (&( im->ImageData[ ( y * im->width + x ) * im->nChannels]));
	}
	else
	{
		Error("x or y out of bounds");
	}
	return NULL;
}

sImage* Histo(sImage* im, uint width, uint height, uint nChannels, uint depth)
{
	int j,i,k,p,q;
	uchar* ptr_im;
	sImage* imHisto;
	int* histo;
	int valeurNbMax=0;
	histo=(int*)malloc(sizeof(int)*256);
	
	/* Calculer l'histogramme */
	for(k=0; k<256; k++)
		histo[k]=0;
	for (j=0; j<im->height; j++)
	{
		for (i=0; i<im->width; i++)
		{
			ptr_im=GetPix(im,i,j);
			histo[ptr_im[0]]++;
			if(histo[ptr_im[0]]>valeurNbMax)
				valeurNbMax = histo[ptr_im[0]];
		}
	}

	/* Create the histogramme image */
	imHisto=CreatesImage(width, height, nChannels, depth);	
	for(k=0; k<imHisto->height * imHisto->width; k++)
		imHisto->ImageData[k]=0;
	for(p=0; p<imHisto->width; p++)
	{
		histo[p]=(histo[p])*imHisto->height/valeurNbMax;
		for(q=0; q<histo[p]; q++)
			imHisto->ImageData[p+(imHisto->height-q)*imHisto->width]=1;
	}
	free(histo);
	return imHisto;
}

sImage* HistoCumul(sImage* im, uint width, uint height, uint nChannels, uint depth)
{
	int j,i,k,p,q;
	uchar* ptr_im;
	sImage* imHistoCumul;
	int* histo;
	int* histoCumul;
	histo=(int*)malloc(sizeof(int)*256);
	histoCumul=(int*)malloc(sizeof(int)*256);
	
	/* Calculer l'histogramme */
	for(k=0; k<256; k++)
		histo[k]=0;
	for (j=0; j<im->height; j++)
	{
		for (i=0; i<im->width; i++)
		{
			ptr_im=GetPix(im,i,j);
			histo[ptr_im[0]]++;
		}
	}
	
	/* Calculer l'histogramme cumulaire */
	for(k=0; k<256; k++)
		histoCumul[k]=0;
	for(p=0; p<256; p++)
		for(q=0; q<=p; q++)
			histoCumul[p]+=histo[q];

	/*	Create the histogramme cumulaire image */
	imHistoCumul=CreatesImage(width, height, nChannels, depth);
	for(k=0; k<imHistoCumul->height * imHistoCumul->width; k++)
		imHistoCumul->ImageData[k]=0;
	for(p=0; p<imHistoCumul->width; p++)
	{
		histoCumul[p]=histoCumul[p]*imHistoCumul->height/histoCumul[255];
		for(q=0; q<histoCumul[p]; q++)
			imHistoCumul->ImageData[p+(imHistoCumul->height-q)*imHistoCumul->width]=1;
	}
	free(histo);
	free(histoCumul);
	return imHistoCumul;
}


void Cumul(sImage* im)
{
	int j,i,k,p,q;
	uchar* ptr_im;
	int* histo;
	int* histoCumul;
	histo=(int*)malloc(sizeof(int)*256);
	histoCumul=(int*)malloc(sizeof(int)*256);
	
	/* Calculer l'histogramme */
	for(k=0; k<256; k++)
		histo[k]=0;
	for (j=0; j<im->height; j++)
	{
		for (i=0; i<im->width; i++)
		{
			ptr_im=GetPix(im,i,j);
			histo[ptr_im[0]]++;
		}
	}
	
	/* Calculer l'histogramme cumulaire */
	for(k=0; k<256; k++)
		histoCumul[k]=0;
	for(p=0; p<256; p++)
		for(q=0; q<=p; q++)
			histoCumul[p]+=histo[q];
	
	/* Appliquer un coefficient pour chaque valeur de HistoCumul :
	   HistoCumulASauver(x)=HistoCumul(x)*256/nb_pixels_dans_image. (Ici, j'ai utilisE histoCumul[255] (la valeur max) ) */
	for(k=0; k<256; k++)
		histoCumul[k]=histoCumul[k]*256/histoCumul[255];
	
	/* Appliquez la transformation suivante sur vos images:
	   Ie(i,j)=HistoCumulASauver(I(i,j));  */
	for (j=0; j<im->height; j++)
	{
		for (i=0; i<im->width; i++)
		{
			ptr_im=GetPix(im,i,j);
			ptr_im[0]=histoCumul[ptr_im[0]];
		}
	}
	free(histo);
	free(histoCumul);
}
